package com.bolt.support.spring.jpa.jpql;

import com.bolt.common.utils.StrUtil;
import com.bolt.support.query.conditions.domain.SFunction;
import com.bolt.support.query.conditions.domain.SqlClause;
import com.bolt.support.query.toolkit.LambdaUtil;
import com.bolt.support.query.toolkit.SerializedLambda;
import com.bolt.support.spring.jpa.entity.PersistableEntity;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class Clauses {


    public static <PersistableEntity, R> ColumnClause<R> of(SFunction<? super PersistableEntity, ? extends R> property) {
        SerializedLambda lambda = LambdaUtil.resolve(property);
        return new ColumnClause<R>(lambda.methodToProperty(), (Class<R>) lambda.getInstantiatedType());
    }




    public static <T extends PersistableEntity> EntityClause<T> of(Class<T> entity) {
        return new EntityClause<T>(entity);
    }

    public static CompareClause of() {
        return new CompareClause();
    }

    public static StitchClause of(String clause) {
        return new StitchClause(clause);
    }

    public static SqlClause of(SqlClause property, Object val) {
        if (val instanceof ColumnClause) {
            return (ColumnClause) val;
        } else {
            return new ParamClause(getParamName(property, null), val);
        }
    }

    public static String getParamName(SqlClause clause, String postfix) {
        String paramName = null;
        if (clause instanceof BaseClause) {
            paramName = ((BaseClause) clause).getParamName();
        }
        if (StrUtil.isNotBlank(postfix)) {
            paramName = paramName + postfix;
        }
        return paramName;
    }
    public static ParamClause of(String paramName, Object val) {
        return new ParamClause<>(paramName, val);
    }


    public static OperationClause and(OperationClause left, OperationClause right) {
        if (left == null) {
            return right;
        } else if (right == null) {
            return left;
        } else {
            return new OperationClause(Ops.AND, left, right);
        }
    }

    public static OperationClause findCompare(Ops operator, List<SqlClause<?>> clauses) {
        OperationClause operationClause = null;
        for (SqlClause<?> sqlClause : clauses) {
            if (sqlClause instanceof OperationClause) {
                OperationClause find = (OperationClause) sqlClause;
                if (find.getOperator() == operator) {
                    operationClause = find;
                    break;
                }
            }
        }
        if (Objects.isNull(operationClause)) {
            operationClause = new OperationClause(operator, new ArrayList<>());
            clauses.add(operationClause);
        }
        return operationClause;

    }

    public static OperationClause addCompare(Ops operator, OperationClause opMixin, SqlClause<?>... clauses) {
        if (opMixin == null) {
            return new OperationClause(Ops.AND, new OperationClause(operator, clauses));
        } else if (operator == Ops.AND) {
            OperationClause operationClause = findCompare(Ops.AND, opMixin.getArgs());
            operationClause.getArgs().addAll(Arrays.asList(clauses));
            return opMixin;
        } else if (operator == Ops.OR) {
            OperationClause operationClause = findCompare(Ops.OR, opMixin.getArgs());
            operationClause.getArgs().addAll(Arrays.asList(clauses));
            return opMixin;
        } else {
            OperationClause newOpMixin = new OperationClause(operator, clauses);
            opMixin.getArgs().add(newOpMixin);
            return opMixin;
        }
    }


    public static List<String> toAliases(List<SqlClause<?>> columnClause) {
        List<String> aliases = new ArrayList<>();
        for (SqlClause<?> column : columnClause) {
            if (column instanceof BaseClause) {
                String alias = ((BaseClause) column).getParamName();
                if (StrUtil.isBlank(alias)) {
                    continue;
                }
                aliases.add(alias);
            }
        }
        return aliases;
    }
}

 